﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Medianamik.Core;
using Medianamik.Core.Synchronization;
using System.IO;
using System.Web.Script.Serialization;
using Medianamik.Core.Sugar;
using Telerik.Web.UI;
using Medianamik.Core.Querying;

namespace Medianamik.UI.SynchronizationManagement
{
    public partial class Synchronization : BasePage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                LoadAllTypes();
        }

        protected void ExportAllNodes_CheckedChanged(object sender, EventArgs e)
        {
            NodesToExportByType.Enabled = !ExportAllNodes.Checked;
            NodesToExport.Enabled = !ExportAllNodes.Checked;
        }

        protected void NodesToExportByType_NodeDataBound(object sender, RadTreeNodeEventArgs e)
        {
            NodeType type = e.Node.DataItem as NodeType;
            var count = type.GetNodeCount();
            e.Node.Text += " (" + count + ", " + type.GetTotalNodeCount() + ")";
            e.Node.Checkable = count > 0;
        }

        protected void NodesToExportByType_NodeCheck(object sender, RadTreeNodeEventArgs e)
        {
            UpdateNodesToExport(e.Node);
        }

        protected void ExportTypesButton_Click(object sender, EventArgs e)
        {
            ExportSelectedTypes();
        }

        protected void SynchronizeTypesButton_Click(object sender, EventArgs e)
        {
            if (IsValid)
            {
                if (TypeUploader.UploadedFiles.Count > 0)
                {
                    SynchronizeTypes(Server.MapPath(TypeUploader.TargetFolder) +
                        TypeUploader.UploadedFiles[0].GetName());
                    DeleteFiles();
                }
            }
        }

        protected void ExportNodesButton_Click(object sender, EventArgs e)
        {
            ExportSelectedNodes();
        }

        protected void SynchronizeNodesButton_Click(object sender, EventArgs e)
        {
            if (IsValid)
            {
                if (NodeUploader.UploadedFiles.Count > 0)
                {
                    SynchronizeNodes(Server.MapPath(NodeUploader.TargetFolder) +
                                     NodeUploader.UploadedFiles[0].GetName());
                    DeleteFiles();
                }
            }
        }

        private void SynchronizeTypes(string filePath)
        {
            var json = Serialization.GetJsonFromFile(filePath);
            var recordTypes = json.FromJson<RecordType>();

            if (recordTypes.Count() <= 0) return;
            
            var synchronizer = new NodeTypeSynchronizer();

            IList<NodeType> types;
            if(synchronizer.Synchronize(recordTypes, out types) && !SimulateSynchronizeType.Checked)
            {
                synchronizer.SaveAll(types);
            }
            //if (SimulateSynchronizeType.Checked)
            //{
            //    synchronizer.SimulateSynchronization(recordTypes);
            //}
            //else
            //    synchronizer.Synchronize(recordTypes);
            
            DisplayExceptions(synchronizer.Exceptions);
            DisplayWarnings(synchronizer.Warnings);
            DisplayLogs(synchronizer.Logs);
        }

        private void LoadAllTypes()
        {
            var types = NodeTypeManager.GetAllTypes(true);

            TypesToExport.DataTextField = "Name";
            TypesToExport.DataValueField = "ID";
            TypesToExport.DataFieldID = "ID";
            TypesToExport.DataFieldParentID = "ParentID";
            TypesToExport.DataSource = types;
            TypesToExport.DataBind();

            NodesToExportByType.DataTextField = "Name";
            NodesToExportByType.DataValueField = "ID";
            NodesToExportByType.DataFieldID = "ID";
            NodesToExportByType.DataFieldParentID = "ParentID";
            NodesToExportByType.DataSource = types.Where(type => !type.IsManyToMany);
            NodesToExportByType.DataBind();
        }

        private void ExportSelectedTypes()
        {
            //Récupérer les types selont les Ids sélectionnés
            IEnumerable<NodeType> typesToExport = TypesToExport.CheckedNodes
                .Select(n => NodeTypeManager.GetNodeType(new Guid(n.Value)));

            //Envoyer le résultat au client
            Response.UploadToClient(typesToExport.ToRecordTypes().ToJson<RecordType>(), "NodeTypes");
        }

        private void SynchronizeNodes(string filePath)
        {
            var json = Serialization.GetJsonFromFile(filePath);
            var records = json.FromJson<Record>();

            if (records.Count() <= 0) return;
            
            var synchronizer = new NodeSynchronizer();

            var warnings = new HashSet<string>();
            var logs = new HashSet<string>();

            IList<Node> nodes;
            if (synchronizer.Synchronize(records, out nodes) && !SimulateSynchronizeNode.Checked)
            {
                NodeManager.SaveNodes(nodes, true);

                if(PublishNodes.Checked)
                {
                    foreach (var record in records)
                    {
                        if (!record.Id.HasValue)
                            warnings.Add("Un élément ayant le nom : " + record.Name +
                                         " n'a pas pu être publié (aucun id).");

                        var cultures = record.DetectCultures();

                        foreach (var culture in cultures)
                        {
                            try
                            {
                                NodeManager.PublishNode(record.Id.Value, culture, DateTime.Now, null);
                                logs.Add("L'élément (" + culture.Name + ") ayant l'ID : " + record.Id.Value +
                                         " a été publié.");
                            }
                            catch (Exception ex)
                            {
                                warnings.Add("L'élément ayant l'ID : " + record.Id.Value +
                                             " n'a pas pu être publié : " + ex.Message);
                            }
                        }
                    }
                }
            }

            DisplayExceptions(synchronizer.Exceptions);
            DisplayWarnings(synchronizer.Warnings.Union(warnings));
            DisplayLogs(synchronizer.Logs.Union(logs));
        }

        private void UpdateNodesToExport(RadTreeNode treeNode)
        {
            if (treeNode.Checked)
            {
                RadTreeNode rootNode = new RadTreeNode(treeNode.Text, treeNode.Value);
                rootNode.Checkable = false;
                NodesToExport.Nodes.Add(rootNode);
                AddLeaves(rootNode);
            }
            else
            {
                RadTreeNode node = NodesToExport.Nodes.FindNodeByValue(treeNode.Value);
                if (node != null)
                    NodesToExport.Nodes.Remove(node);
            }
        }

        private static void AddLeaves(RadTreeNode rootNode)
        {
            //Working list
            List<Node> nodes = NodeManager.GetNodesByType(new Guid(rootNode.Value), true);

            //Aller chercher les noeuds qui n'ont pas de parents dans la liste (nodes)
            IEnumerable<Node> rootNodes = nodes.Where(childNode =>
                !nodes.Any(parentNode => parentNode.ID.Equals(childNode.ParentID)));

            //Ajouter les noeuds au noeud recine
            rootNodes.ForEach(n => rootNode.Nodes.Add(new RadTreeNode(n.DisplayName, n.ID.ToString())));

            //Retirer les noeuds du working list
            List<Guid> temp = new List<Guid>(rootNodes.Select(n => n.ID));
            nodes.RemoveAll(n => temp.Any(id => n.ID.Equals(id)));

            //Rendu ici il reste juste les noeuds avec des parents
            int level = 1;
            while (nodes.Count > 0)
            {
                //Boucler sur chacun des noeuds à un niveau (level) de l'arbre
                foreach (var node in rootNode.TreeView.GetAllNodes().Where(n => n.Level.Equals(level)))
                {
                    //Trouver les enfants, les ajouter au RadTreeView et les supprimer de nodes
                    var nodesToRemove = new List<Node>();
                    foreach (var item in nodes.Where(n => n.ParentID.ToString().Equals(node.Value)))
                    {
                        node.Nodes.Add(new RadTreeNode(item.DisplayName, item.ID.ToString()));
                        nodesToRemove.Add(item);

                    }
                    foreach (var item in nodesToRemove)
                    {
                        nodes.Remove(item);
                    }

                }

                level++;
            }
        }

        private void ExportSelectedNodes()
        {
            List<Node> nodesToExport = null;

            if (ExportAllNodes.Checked)
            {
                nodesToExport = new Query().Execute().ToList();
            }
            else
            {
                List<Guid> nodeIds = new List<Guid>();

                for (int i = 0; i < NodesToExport.Nodes.Count; i++)
                {
                    var checkedNodes = FindCheckedNodes(NodesToExport.Nodes[i]).ToList();

                    foreach (var checkedNode in checkedNodes)
                    {
                        nodeIds.Add(new Guid(checkedNode.Value));
                    }
                }

                nodesToExport = NodeManager.GetNodes(nodeIds.ToArray()).ToList();
            }

            //Envoyer le résultat au client
            Response.UploadToClient(nodesToExport.ToRecords().ToJson<Record>(), "Nodes");
        }

        /// <summary>
        /// Cherche les Noeuds sélectionnés de manière récursive
        /// </summary>
        private static IEnumerable<RadTreeNode> FindCheckedNodes(IRadTreeNodeContainer pRootNode)
        {
            foreach (var node in pRootNode.Nodes.Cast<RadTreeNode>())
            {
                if (node.Checked) { yield return node; }
                foreach (var subNode in FindCheckedNodes(node))
                {
                    yield return subNode;
                }
            }
        }

        private void DisplayExceptions(IEnumerable<string> exceptions)
        {
            DisplayList(exceptions, "Liste des exceptions (" + exceptions.Count() + ")", ExceptionLog);
        }
        private void DisplayWarnings(IEnumerable<string> warnings)
        {
            DisplayList(warnings, "Liste des avertissements (" + warnings.Count() + ")", WarningLog);
        }
        private void DisplayLogs(IEnumerable<string> logs)
        {
            DisplayList(logs, "Liste des modifications (" + logs.Count() + ")", Log);
        }

        private static void DisplayList(IEnumerable<string> list, string title, ITextControl literal)
        {
            var builder = new StringBuilder("<h3>" + title + "</h3>\n");

            if (list.Count() > 0)
            {
                builder.AppendLine("<ul>");
                list.ForEach(ex =>
                    builder.AppendLine("<li>" + ex + "</li>"));
                builder.AppendLine("</ul>");
                literal.Text = builder.ToString();
            }

            literal.Text = builder.ToString();
        }

        private void DeleteFiles()
        {
            var targetFolder = Server.MapPath(TypeUploader.TargetFolder);

            var targetDir = new DirectoryInfo(targetFolder);

            foreach (var file in targetDir.GetFiles())
            {
                if ((file.Attributes & FileAttributes.ReadOnly) == 0) file.Delete();
            }
        }
    }
}